<?php

namespace Hilaoyu\LaravelExtend\Services\Files\Controllers;

use Hilaoyu\LaravelExtend\ControllerBase;
use Hilaoyu\LaravelExtend\Services\Files\FileService;
use Hilaoyu\LaravelExtend\Supports\Uuid;
use Hilaoyu\Utils\UtilFile;
use Hilaoyu\Utils\UtilStr;
use Hilaoyu\Utils\UtilSupport;
use Illuminate\Http\Request;


class FileUploadController extends ControllerBase
{


    public function __construct()
    {
        UtilSupport::tryQuiet(function () {
            parent::__construct();
        });
    }

    /**
     * 获取取上传接口
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function uploader(Request $request, $disk = '')
    {
        $urlAbsolute = (bool)config('file_service.uploader_url_absolute',0);
        $limit_size = (int)config('filesystems.disks.'.$disk.'.allow_upload.max_size',0);
        $limit_ext = (array)config('filesystems.disks.'.$disk.'.allow_upload.allow_ext',[]);
        $limits_tip ="";
        if(!empty($limit_ext)){
            $limits_tip .= "允许的文件类型为：".implode(',',$limit_ext).";";
        }
        if($limit_size > 0){
            $limits_tip .= "单个文件大小不超过：".UtilFile::sizeFormat($limit_size).";";
        }
        $appUrl = app('url');
        $data = [
            'task_add'=>$appUrl->route('api.l-e-s.files.upload.task.add', ['disk' => $disk],$urlAbsolute),
            'task_get'=>$appUrl->route('api.l-e-s.files.upload.task.view',[],$urlAbsolute),
            'task_complete'=>$appUrl->route('api.l-e-s.files.upload.task.complete',[],$urlAbsolute),
            'limit_size'=>$limit_size,
            'limit_exts'=>$limit_ext,
            'limit_tip'=>$limits_tip,
        ];
        if($client = FileService::useAsClient($disk)){
            $uploader = $client->uploader();
            if(!$uploader){
                return comResponse()->failed($client->getResult('message'))
                    ->withErrors($client->getError())
                    ->json();
            }
            $data['upload'] = $uploader;
        }else{
            $data['upload'] = $appUrl->route('api.l-e-s.files.upload', ['disk' => $disk],$urlAbsolute);
        }

        return comResponse()->success()
            ->with($data)
            ->json();
    }


    /**
     * 生成上传任务
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function uploadTaskCreate(Request $request, $disk = '',$encrypter=null)
    {
        $task = [];

        if($client = FileService::useAsClient($disk)){
            $task = $client->uploadTaskCreate();
            if(!$task){
                return comResponse()->failed($client->getResult('message'))
                    ->withErrors($client->getError())
                    ->json();
            }
        }
        if (!$filesModel = FileService::newFilesModel($disk,$task)) {

            return comResponse()->failed('创建任务失败')
                ->withErrors($filesModel->getError())
                ->json();
        }
        if($id = data_get($task,'id','')){
            $filesModel->id = $id;
        }

        $app = $request->route('_auth_app');
        $filesModel->fill(['app_id'=>data_get($app,'id')]);

        if(! $filesModel->saveToCache()){
            return comResponse()->failed('保存任务失败')
                ->withErrors($filesModel->getError())
                ->json();
        }
        $filesModel->makeHidden(['url']);
        return comResponse()->success()
            ->with($filesModel,null,$encrypter)
            ->json();
    }

    /**
     * 读取上传任务
     * @param Request $request
     * @param string $id filesModel ID
     * @return \Illuminate\Http\JsonResponse
     */
    public function uploadTaskGet(Request $request, $id = '',$encrypter=null)
    {
        if(!$id){
            $id = $request->input('id','');
        }
        if (!$id || !$filesModel = FileService::loadFilesModel($id)) {
            abort(404);
        }

        $disk = $filesModel->getAttribute('disk');

        if($client = FileService::useAsClient($disk)){
            if(!$attributes = $client->uploadTaskGet($id)){
                return comResponse()->failed($client->getResult('message'))
                    ->withErrors($client->getError())
                    ->json();
            }
            unset($attributes['disk']);
            unset($attributes['app_id']);
            $filesModel->fill($attributes);
            if($filesModel->getAttribute('is_permanent')){
                $filesModel->save();
            }else{
                $filesModel->saveToCache();
            }
        }

        if('1' != $filesModel->getAttribute('upload_is_completed')){
            $filesModel->makeHidden(['url']);
        }

        return comResponse()->success()
            ->with($filesModel,null,$encrypter)
            ->json();
    }
    /**
     * 完成上传任务
     * @param Request $request
     * @param string $id filesModel ID
     * @return \Illuminate\Http\JsonResponse
     */
    public function uploadTaskComplete(Request $request, $id = '')
    {
        if(!$id){
            $id = $request->input('id','');
        }
        if (!$id || !$filesModel = FileService::loadFilesModel($id)) {
            abort(404);
        }

        $disk = $filesModel->getAttribute('disk');



        $service_file_url = '';
        if($client = FileService::useAsClient($disk)){
            if(!$attributes = $client->uploadTaskGet($id)){
                return comResponse()->failed($client->getResult('message'))
                    ->withErrors($client->getError())
                    ->json();
            }

            unset($attributes['disk']);
            unset($attributes['app_id']);
            $service_file_url = data_get($attributes,'url','');
            $attributes['path'] = $service_file_url;
            $filesModel->fill($attributes);


        }

        $status = false;
        if($filesModel->getAttribute('is_permanent')){
            $status = $filesModel->save();
        }else{
            $status = $filesModel->saveToCache();
        }

        if(! $status){
            return comResponse()->failed('保存任务失败')
                ->withErrors($filesModel->getError())
                ->json();
        }
        if('1' != $filesModel->getAttribute('upload_is_completed')){
            return comResponse()->failed('上传任务还未完成')
                ->withErrors($filesModel->getError())
                ->json();
        }

        if(FileService::getDiskConfig($disk,'use_after_upload',null)){
            (new FileService("","",$filesModel))->queueUseFor();
        }

        $urlAbsolute = $request->input('url_absolute','');
        if('1' == $urlAbsolute){
            $filesModel->setUrlAbsolute(true);
        }

        $data = $filesModel->toArray();
        if($service_file_url && '1' == $urlAbsolute && 'public' == FileService::getDiskConfig($filesModel->getAttribute('disk'),'visibility')){
            data_set($data,'url',$service_file_url);
        }

        return comResponse()->success()
            ->with($data)
            ->json();
    }

    /**
     * 文件上传，支持分片，兼容百度webUploader
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function upload(Request $request, $id = '')
    {
        //comResponse()->with($request->input('id', ''),'id');

        if (!$filesModel = FileService::loadFilesModel($id)) {
            abort(404);
        }
        $task_id = $filesModel->getKey();
        $disk = $filesModel->getAttribute('disk');

        if (!$upload_config = FileService::getDiskConfig($disk, 'allow_upload', null)) {
            abort(404);
        }

        if (1 == $filesModel->getAttribute('upload_is_completed')) {
            return comResponse()->with($filesModel)->success('成功', 20201)->json();
        }
        try {

            if(!$storage = FileService::makeStorage(FileService::tempDisk())){
                throw new \Exception('系统错误，请稍后重试');
            }

            $upload_task = $filesModel->getAttribute('upload_task');

            //整理参数
            $file_size = intval($request->input('size', 0));

            $chunks = intval($request->input('chunks', 0));
            if ($chunks < 1) {
                $chunks = 1;
            }
            $chunk = intval($request->input('chunk', 0));
            if ($chunk < 0) {
                $chunk = 0;
            }
            $chunk_size = intval($request->input('chunk_size', 0));

            //检查是否超出大小限制
            $size_limit = data_get($upload_config, 'max_size', 0);
            if ($size_limit > 0 && $file_size > 0 && $size_limit < $file_size) {
                throw new \Exception('只允许上传小于' . UtilFile::sizeFormat($size_limit) . '的文件');
            }


            //检查临时文件是否存在
            $temp_file = data_get($_FILES, 'upload_file');
            if (!$temp_file) {
                throw new \Exception('上传文件不存在');
            }

            //检查临时文件是否有错误信息
            if (!empty($temp_file['error'])) {
                throw new \Exception($temp_file['error']);
            }


            $temp_file_path = isset($temp_file['tmp_name']) ? trim($temp_file['tmp_name']) : '';
            $temp_file_size = isset($temp_file['size']) ? intval($temp_file['size']) : 0;

            //检查临时文件是否存在 且大小是否为0
            if (!is_file($temp_file_path)
                || $temp_file_size <= 0
            ) {
                throw new \Exception('上传文件错误');
            }


            //取得文件名,扩展名，和mime type
            $file_name = trim($request->input('name', data_get($temp_file, 'name', '')));

            $file_ext = UtilFile::getExtension($file_name);

            $file_mime = trim($request->input('type', data_get($temp_file, 'type', '')));


            //检查文件扩展名
            $allow_ext = data_get($upload_config, 'allow_ext', []);

            if (!empty($allow_ext) && !in_array(UtilStr::lower($file_ext), $allow_ext)) {
                throw new \Exception('只允许上传扩展名为 ' . implode(',', $allow_ext) . ' 的文件');
            }



            //更新任务数据

            if (!$filesModel->getAttribute('name')) {
                $filesModel->fill(['name'=>$file_name]);
            }
            if (!$filesModel->getAttribute('ext')) {
                $filesModel->fill(['ext'=>$file_ext]);
            }
            if (!$filesModel->getAttribute('size')) {
                $filesModel->fill(['size'=>$file_size]);
            }
            if (!$filesModel->getAttribute('mime')) {
                $filesModel->fill([
                    'mime'=>$file_mime,
                    'type'=> UtilFile::mineToNormalType($file_mime),
                ]);
            }


            if (!isset($upload_task['chunks']) || empty($upload_task['chunks'])) {
                $upload_task['chunks'] = $chunks;
            }
            if (!isset($upload_task['chunk_size']) || empty($upload_task['chunk_size'])) {
                $upload_task['chunk_size'] = $chunk_size;
            }

            $upload_task['chunk'] = $chunk;


            //取得临时存储区真实路径 绝对路径

            $chunk_save_path = $storage->path(
                FileService::generateHashPath('upload_chunks/' . $task_id, $chunk, 'chunk')
            );

            $chunk_save_dir = UtilFile::dirname($chunk_save_path);
            if (!UtilFile::checkDir($chunk_save_dir)) {
                throw new \Exception('临时存储目录不可写');
            }

            //移动保存分片
            if (!move_uploaded_file($temp_file_path, $chunk_save_path)) {
                throw new \Exception('保存临时文件出错');
            }

            //更新任务数据
            $upload_task['chunks_completed'][$chunk] = $chunk;
            $upload_task['chunks_completed_count'] = count($upload_task['chunks_completed']);
            $upload_task['chunks_completed_percent'] = max($upload_task['chunks_completed_percent'], intval($upload_task['chunks_completed_count'] / $chunks * 100));


            $done = true;
            for ($index = 0; $index < $chunks; $index++) {
                $chunk_file = FileService::generateHashPath('upload_chunks/' . $task_id, $index, 'chunk');
                if (!$storage->has($chunk_file)) {
                    $done = false;
                    break;
                }
            }

            $code = 0 ;
            if ($done) {

                $completed_save_path = $filesModel->getAttribute('path').'.'.$file_ext;

                if ($storage->has($completed_save_path)) {
                    $completed_save_path .= uniqid('_');
                    //$storage->delete($completed_save_path);
                }

                $completed_save_dir = UtilFile::dirname($completed_save_path);

                if (!UtilFile::checkDir($storage->path($completed_save_dir))) {
                    throw new \Exception('存储目录不可写');
                }

                //dd($completed_save_path);
                $completed_save_handler = fopen($storage->path($completed_save_path), "awb+");
                for ($index = 0; $index < $chunks; $index++) {
                    $chunk_file = FileService::generateHashPath('upload_chunks/' . $task_id, $index, 'chunk');
                    if ($storage->has($chunk_file)) {
                        $chunk_file_size = @filesize($storage->path($chunk_file));
                        //dump($storage->path($chunk_file), $chunk_file_size);

                        $chunk_read_handler = fopen($storage->path($chunk_file), "rb");

                        if (@fwrite($completed_save_handler, fread($chunk_read_handler, $chunk_file_size)) === false) {
                            throw new \Exception('合并文件出错');
                        }
                        @fclose($chunk_read_handler);

                    } else {
                        throw new \Exception('分片文件' . $index . '不存在');
                    }
                }

                @fclose($completed_save_handler);


                $save_file_size = $storage->size($completed_save_path);
                //再次检查是否超出大小限制
                if ($size_limit > 0 && $size_limit < $save_file_size) {
                    UtilFile::delDirOrFile($storage->path($completed_save_path));
                    throw new \Exception('只允许上传小于' . UtilFile::sizeFormat($size_limit) . '的文件');

                }

                $filesModel->fill([
                    'size'=>$save_file_size
                ]);
                UtilFile::delDirOrFile($chunk_save_dir);


                //更新任务数据
                $upload_task['completed'] = true;
                $upload_task['chunks_completed'] = array_keys(array_fill(0, $chunks, ''));
                $upload_task['chunks_completed_count'] = $chunks;
                $upload_task['chunks_completed_percent'] = 100;

                $filesModel->fill([
                    'upload_is_completed'=>1,
                    'path'=>$completed_save_path,
                    'hash_md5'=> UtilFile::hash($storage->path($completed_save_path),'md5'),
                ]);

                $code = 20201;

            }

            $filesModel->fill(['upload_task'=>$upload_task]);
            $filesModel->saveToCache();
            $filesModel->makeHidden(['url']);
            return comResponse()->with($filesModel)->success('成功', $code)->json();
        } catch (\Exception $exception) {
            return comResponse()->failed($exception->getMessage(),20403)
                ->withErrors($filesModel->getError())
                ->json();
        }


    }



}
